/*-------------------------------------------------------------------------
 *
 * mbutils.c
 *	  此文件包含编码转换的函数。
 *
 * 该文件中的字符串转换函数共享一些 API 特性。
 * 注意以下几点：
 *
 * 如果需要转换，函数返回一个 palloc 的、以 NULL 结尾的字符串。
 * 然而，如果没有进行转换，则返回给定的源字符串指针不变。
 *
 * 尽管存在长度参数，意味着调用者可以传递非 NULL 结尾的字符串，但需要小心，
 * 因为如果没有发生转换，同样的字符串将被返回。
 * 这样的调用者 *必须* 检查 result == src，并以不同方式处理该情况。
 *
 * 如果源编码和目标编码相同，则源字符串在没有任何验证的情况下返回；
 * 假定其为有效数据。如果可能不是这种情况，调用者有责任使用单独的调用验证 
 * 字符串 pg_verify_mbstr()。每当源编码和目标编码不同，函数确保结果根据目标编码有效编码。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/mb/mbutils.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/xact.h"
#include "catalog/namespace.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/syscache.h"

/*
 * 我们维护一个简单的链表，缓存当前选定的转换函数的fmgr查找信息，
 * 以及在当前会话中之前选择的任何转换函数。 （我们记住之前的
 * 设置，因为我们必须能够在事务回滚期间恢复之前的
 * 设置，而不进行任何新的目录访问。）
 *
 * 由于我们永远不会释放这些数据，我们只是将其保存在TopMemoryContext中。
 */
typedef struct ConvProcInfo
{
	int			s_encoding;		/* 服务器和客户端编码ID */
	int			c_encoding;
	FmgrInfo	to_server_info; /* 转换过程的查找信息 */
	FmgrInfo	to_client_info;
} ConvProcInfo;

static List *ConvProcList = NIL;	/* ConvProcInfo列表 */

/*
 * 这些变量指向当前活动的转换函数，
 * 或在不需要转换时为NULL。
 */
static FmgrInfo *ToServerConvProc = NULL;
static FmgrInfo *ToClientConvProc = NULL;

/*
 * 这个变量存储用于从UTF-8转换为服务器编码的转换函数。
 * 如果服务器编码是UTF-8，或者我们缺乏该转换函数，则为NULL。
 */
static FmgrInfo *Utf8ToServerConvProc = NULL;

/*
 * 这些变量跟踪当前选定的编码。
 */
static const pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
static const pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
static const pg_enc2name *MessageEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];

/*
 * 在后端启动期间，我们无法设置客户端编码，因为我们（a）
 * 无法查找转换函数，并且（b）可能尚不知道数据库
 * 编码。因此，SetClientEncoding()只接受任何值，并
 * 记住，以便InitializeClientEncoding()稍后应用。
 */
static bool backend_startup_complete = false;
static int	pending_client_encoding = PG_SQL_ASCII;


/* 内部函数 */
static char *fc_perform_default_encoding_conversion(const char *fc_src,
												 int fc_len, bool fc_is_client_to_server);
static int	fc_cliplen(const char *fc_str, int fc_len, int fc_limit);


/*
 * 为将来调用SetClientEncoding做准备。成功意味着
 * SetClientEncoding对于此编码请求保证成功。
 *
 * （但请注意，在backend_startup_complete之前的成功不保证
 * 之后成功……）
 *
 * 如果正常返回0，如果不正常返回-1（编码错误或不支持转换）
 */
int PrepareClientEncoding(int fc_encoding)
{
	int			fc_current_server_encoding;
	ListCell   *fc_lc;

	if (!PG_VALID_FE_ENCODING(fc_encoding))
		return -1;

	/* 在启动期间无法执行任何操作，如上所述 */
	if (!backend_startup_complete)
		return 0;

	fc_current_server_encoding = GetDatabaseEncoding();

	/*
	 * 检查不需要转换函数的情况。
	 */
	if (fc_current_server_encoding == fc_encoding ||
		fc_current_server_encoding == PG_SQL_ASCII ||
		fc_encoding == PG_SQL_ASCII)
		return 0;

	if (IsTransactionState())
	{
		/*
		 * 如果我们在一个活跃的事务中，安全地访问目录，
		 * 因此查找函数。即使信息已经缓存，我们也会重复查找，以便
		 * 响应pg_conversion内容的变化。
		 */
		Oid			fc_to_server_proc,
					fc_to_client_proc;
		ConvProcInfo *fc_convinfo;
		MemoryContext fc_oldcontext;

		fc_to_server_proc = FindDefaultConversionProc(fc_encoding,
												   fc_current_server_encoding);
		if (!OidIsValid(fc_to_server_proc))
			return -1;
		fc_to_client_proc = FindDefaultConversionProc(fc_current_server_encoding,
												   fc_encoding);
		if (!OidIsValid(fc_to_client_proc))
			return -1;

		/*
		 * 将fmgr信息加载到TopMemoryContext（这里仍然可能失败）
		 */
		fc_convinfo = (ConvProcInfo *) MemoryContextAlloc(TopMemoryContext,
													   sizeof(ConvProcInfo));
		fc_convinfo->s_encoding = fc_current_server_encoding;
		fc_convinfo->c_encoding = fc_encoding;
		fmgr_info_cxt(fc_to_server_proc, &fc_convinfo->to_server_info,
					  TopMemoryContext);
		fmgr_info_cxt(fc_to_client_proc, &fc_convinfo->to_client_info,
					  TopMemoryContext);

		/* 将新信息附加到列表头部 */
		fc_oldcontext = MemoryContextSwitchTo(TopMemoryContext);
		ConvProcList = lcons(fc_convinfo, ConvProcList);
		MemoryContextSwitchTo(fc_oldcontext);

		/*
		 * 我们还不能移除任何旧的相同编码对的条目，
		 * 因为它仍然可能在使用中。SetClientEncoding 将进行清理。
		 */

		return 0;				/* 成功 */
	}
	else
	{
		/*
		 * 如果我们不在活动事务中，我们能做的唯一事情是
		 * 使用缓存恢复先前的设置。这覆盖了所有
		 * 事务回滚的情况。唯一可能无法工作的情况是
		 * 通过编辑 postgresql.conf 和发送 SIGHUP 来实时更改 client_encoding。
		 * 无论如何，这可能都是一件愚蠢的事。
		 */
		foreach(fc_lc, ConvProcList)
		{
			ConvProcInfo *fc_oldinfo = (ConvProcInfo *) lfirst(fc_lc);

			if (fc_oldinfo->s_encoding == fc_current_server_encoding &&
				fc_oldinfo->c_encoding == fc_encoding)
				return 0;
		}

		return -1;				/* 它没有被缓存，因此失败 */
	}
}

/*
 * 设置活动的客户端编码并设置转换函数指针。
 * 应该之前已经为该编码调用 PrepareClientEncoding。
 *
 * 如果没问题返回 0，如果不可以返回 -1（编码不正确或无法支持转换）
 */
int SetClientEncoding(int fc_encoding)
{
	int			fc_current_server_encoding;
	bool		fc_found;
	ListCell   *fc_lc;

	if (!PG_VALID_FE_ENCODING(fc_encoding))
		return -1;

	/* 在启动期间无法执行任何操作，如上所述 */
	if (!backend_startup_complete)
	{
		pending_client_encoding = fc_encoding;
		return 0;
	}

	fc_current_server_encoding = GetDatabaseEncoding();

	/*
	 * 检查不需要转换函数的情况。
	 */
	if (fc_current_server_encoding == fc_encoding ||
		fc_current_server_encoding == PG_SQL_ASCII ||
		fc_encoding == PG_SQL_ASCII)
	{
		ClientEncoding = &pg_enc2name_tbl[fc_encoding];
		ToServerConvProc = NULL;
		ToClientConvProc = NULL;
		return 0;
	}

	/*
	 * 在缓存中搜索之前由
	 * PrepareClientEncoding 准备的条目；如果没有，就失去了。与此同时，
	 * 释放任何重复条目，以便重复的 Prepare/Set 循环不会
	 * 泄漏内存。
	 */
	fc_found = false;
	foreach(fc_lc, ConvProcList)
	{
		ConvProcInfo *fc_convinfo = (ConvProcInfo *) lfirst(fc_lc);

		if (fc_convinfo->s_encoding == fc_current_server_encoding &&
			fc_convinfo->c_encoding == fc_encoding)
		{
			if (!fc_found)
			{
				/* 找到最新的条目，因此进行设置 */
				ClientEncoding = &pg_enc2name_tbl[fc_encoding];
				ToServerConvProc = &fc_convinfo->to_server_info;
				ToClientConvProc = &fc_convinfo->to_client_info;
				fc_found = true;
			}
			else
			{
				/* 重复条目，释放它 */
				ConvProcList = foreach_delete_current(ConvProcList, fc_lc);
				pfree(fc_convinfo);
			}
		}
	}

	if (fc_found)
		return 0;				/* 成功 */
	else
		return -1;				/* 它没有被缓存，因此失败 */
}

/*
 * 初始化客户端编码转换。
 *		从 InitPostgres() 在后端启动期间调用一次。
 */
void InitializeClientEncoding(void)
{
	int			fc_current_server_encoding;

	Assert(!backend_startup_complete);
	backend_startup_complete = true;

	if (PrepareClientEncoding(pending_client_encoding) < 0 ||
		SetClientEncoding(pending_client_encoding) < 0)
	{
		/*
		 * 哎呀，请求的转换不可用。我们之前无法失败，
		 * 但现在可以。
		 */
		ereport(FATAL,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("conversion between %s and %s is not supported",
						pg_enc2name_tbl[pending_client_encoding].name,
						GetDatabaseEncodingName())));
	}

	/*
	 * 如果需要，也查找 UTF8 到服务器的转换函数。由于
	 * 服务器编码在任何一个后端进程中都是固定的，我们不
	 * 必须做不止一次。
	 */
	fc_current_server_encoding = GetDatabaseEncoding();
	if (fc_current_server_encoding != PG_UTF8 &&
		fc_current_server_encoding != PG_SQL_ASCII)
	{
		Oid			fc_utf8_to_server_proc;

		Assert(IsTransactionState());
		fc_utf8_to_server_proc =
			FindDefaultConversionProc(PG_UTF8,
									  fc_current_server_encoding);
		/* 如果没有这样的转换，只需将指针保留为 NULL */
		if (OidIsValid(fc_utf8_to_server_proc))
		{
			FmgrInfo   *fc_finfo;

			fc_finfo = (FmgrInfo *) MemoryContextAlloc(TopMemoryContext,
													sizeof(FmgrInfo));
			fmgr_info_cxt(fc_utf8_to_server_proc, fc_finfo,
						  TopMemoryContext);
			/* 只有在数据完全有效后才设置 Utf8ToServerConvProc */
			Utf8ToServerConvProc = fc_finfo;
		}
	}
}

/*
 * 返回当前客户端编码
 */
int pg_get_client_encoding(void)
{
	return ClientEncoding->encoding;
}

/*
 * 返回当前客户端编码名称
 */
const char * pg_get_client_encoding_name(void)
{
	return ClientEncoding->name;
}

/*
 * 将 src 字符串转换为另一种编码（一般情况）。
 *
 * 请参见本文件顶部关于字符串转换函数的说明。
 */
unsigned char * pg_do_encoding_conversion(unsigned char *fc_src, int fc_len,
						  int fc_src_encoding, int fc_dest_encoding)
{
	unsigned char *fc_result;
	Oid			fc_proc;

	if (fc_len <= 0)
		return fc_src;				/* 空字符串总是有效的 */

	if (fc_src_encoding == fc_dest_encoding)
		return fc_src;				/* 无需转换，假设有效 */

	if (fc_dest_encoding == PG_SQL_ASCII)
		return fc_src;				/* 任何字符串在 SQL_ASCII 中都是有效的 */

	if (fc_src_encoding == PG_SQL_ASCII)
	{
		/* 无法进行转换，但我们必须验证结果 */
		(void) pg_verify_mbstr(fc_dest_encoding, (const char *) fc_src, fc_len, false);
		return fc_src;
	}

	if (!IsTransactionState())	/* 不应该发生 */
		elog(ERROR, "cannot perform encoding conversion outside a transaction");

	fc_proc = FindDefaultConversionProc(fc_src_encoding, fc_dest_encoding);
	if (!OidIsValid(fc_proc))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
						pg_encoding_to_char(fc_src_encoding),
						pg_encoding_to_char(fc_dest_encoding))));

	/*
	 * 为转换结果分配空间，要小心整数溢出。
	 *
	 * len * MAX_CONVERSION_GROWTH 通常是所需空间的巨大高估，
	 * 因此它可能超过 MaxAllocSize，即使结果实际上可以放下。我们不想返回
	 * 超过 MaxAllocSize 的结果字符串，因为调用方可能不能优雅地处理——但是
	 * 如果我们确实分配了超过 MaxAllocSize 的内存而没有使用，那也可以。
	 */
	if ((Size) fc_len >= (MaxAllocHugeSize / (Size) MAX_CONVERSION_GROWTH))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("out of memory"),
				 errdetail("String of %d bytes is too long for encoding conversion.",
						   fc_len)));

	fc_result = (unsigned char *)
		MemoryContextAllocHuge(CurrentMemoryContext,
							   (Size) fc_len * MAX_CONVERSION_GROWTH + 1);

	(void) OidFunctionCall6(fc_proc,
							Int32GetDatum(fc_src_encoding),
							Int32GetDatum(fc_dest_encoding),
							CStringGetDatum(fc_src),
							CStringGetDatum(fc_result),
							Int32GetDatum(fc_len),
							BoolGetDatum(false));

	/*
	 * 如果结果很大，值得进行重新分配以释放我们请求的任何额外
	 * 空间。这里的截止点有点任意，但我们*必须*检查当 len * MAX_CONVERSION_GROWTH 超过 MaxAllocSize 时。
	 */
	if (fc_len > 1000000)
	{
		Size		fc_resultlen = strlen((char *) fc_result);

		if (fc_resultlen >= MaxAllocSize)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("out of memory"),
					 errdetail("String of %d bytes is too long for encoding conversion.",
							   fc_len)));

		fc_result = (unsigned char *) repalloc(fc_result, fc_resultlen + 1);
	}

	return fc_result;
}

/*
 * 将 src 字符串转换为另一种编码。
 *
 * 此函数的 API 与其他转换函数不同。
 * 调用方应该使用 FindDefaultConversionProc() 查找转换函数。
 * 与其他函数不同，转换结果不是 palloc 的。
 * 它是写入调用方提供的缓冲区。
 *
 * src_encoding   - 要转换的编码
 * dest_encoding  - 要转换到的编码
 * src, srclen    - 输入缓冲区及其字节长度
 * dest, destlen  - 目标缓冲区及其字节大小
 *
 * 输出以空字符结尾。
 *
 * 如果 destlen < srclen * MAX_CONVERSION_LENGTH + 1，则转换的输出
 * 不一定能够适应输出缓冲区，函数将不会转换整个输入。
 *
 * TODO: 转换函数接口并不理想。首先，将目标缓冲区的大小传递给
 * 转换函数是个不错的主意，这样如果你传递了较短的目标缓冲区，它
 * 仍然可以继续填充整个缓冲区。目前，我们必须假设最坏情况下的扩展并提前停止转换，即使在目标缓冲区中实际上还有空间。其次，将写入的字节数返回给调用方也是很好的，以避免调用 strlen()。
 */
int pg_do_encoding_conversion_buf(Oid fc_proc,
							  int fc_src_encoding,
							  int fc_dest_encoding,
							  unsigned char *fc_src, int fc_srclen,
							  unsigned char *fc_dest, int fc_destlen,
							  bool fc_noError)
{
	Datum		fc_result;

	/*
	 * 如果目标缓冲区不足以容纳结果的最坏情况，
	 * 限制传递给转换函数的输入大小。
	 */
	if ((Size) fc_srclen >= ((fc_destlen - 1) / (Size) MAX_CONVERSION_GROWTH))
		fc_srclen = ((fc_destlen - 1) / (Size) MAX_CONVERSION_GROWTH);

	fc_result = OidFunctionCall6(fc_proc,
							  Int32GetDatum(fc_src_encoding),
							  Int32GetDatum(fc_dest_encoding),
							  CStringGetDatum(fc_src),
							  CStringGetDatum(fc_dest),
							  Int32GetDatum(fc_srclen),
							  BoolGetDatum(fc_noError));
	return DatumGetInt32(fc_result);
}

/*
 * 将字符串转换为编码 encoding_name。源
 * 编码是数据库编码。
 *
 * BYTEA convert_to(TEXT string, NAME encoding_name) */
Datum pg_convert_to(PG_FUNCTION_ARGS)
{
	Datum		fc_string = PG_GETARG_DATUM(0);
	Datum		fc_dest_encoding_name = PG_GETARG_DATUM(1);
	Datum		fc_src_encoding_name = DirectFunctionCall1(namein,
														CStringGetDatum(DatabaseEncoding->name));
	Datum		fc_result;

	/*
	 * pg_convert 期望第一个参数是 bytea。我们在这里传入一个
	 * 文本参数，依赖于它们实际上都是 varlena 类型，因此结构上相同。
	 */
	fc_result = DirectFunctionCall3(pg_convert, fc_string,
								 fc_src_encoding_name, fc_dest_encoding_name);

	PG_RETURN_DATUM(fc_result);
}

/*
 * 将字符串从编码 encoding_name 转换。目标
 * 编码是数据库编码。
 *
 * TEXT convert_from(BYTEA string, NAME encoding_name) */
Datum pg_convert_from(PG_FUNCTION_ARGS)
{
	Datum		fc_string = PG_GETARG_DATUM(0);
	Datum		fc_src_encoding_name = PG_GETARG_DATUM(1);
	Datum		fc_dest_encoding_name = DirectFunctionCall1(namein,
														 CStringGetDatum(DatabaseEncoding->name));
	Datum		fc_result;

	fc_result = DirectFunctionCall3(pg_convert, fc_string,
								 fc_src_encoding_name, fc_dest_encoding_name);

	
/*
	 * pg_convert 返回一个 bytea，我们然后将其作为文本返回，依赖于
	 * 这两者实际上都是 varlena 类型，因此
	 * 结构上是相同的。虽然不是所有的 bytea 值都是有效文本，
	 * 在这种情况下，它将是因为我们已经告诉 pg_convert 返回一个
	 * 在当前数据库编码中有效的文本。
	 */
	PG_RETURN_DATUM(fc_result);
}

/*
 * 在两种任意编码之间转换字符串。
 *
 * BYTEA convert(BYTEA string, NAME src_encoding_name, NAME dest_encoding_name)
 */
Datum pg_convert(PG_FUNCTION_ARGS)
{
	bytea	   *fc_string = PG_GETARG_BYTEA_PP(0);
	char	   *fc_src_encoding_name = NameStr(*PG_GETARG_NAME(1));
	int			fc_src_encoding = pg_char_to_encoding(fc_src_encoding_name);
	char	   *fc_dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
	int			fc_dest_encoding = pg_char_to_encoding(fc_dest_encoding_name);
	const char *fc_src_str;
	char	   *fc_dest_str;
	bytea	   *fc_retval;
	int			fc_len;

	if (fc_src_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid source encoding name \"%s\"",
						fc_src_encoding_name)));
	if (fc_dest_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid destination encoding name \"%s\"",
						fc_dest_encoding_name)));

	/* 确保源字符串有效 */
	fc_len = VARSIZE_ANY_EXHDR(fc_string);
	fc_src_str = VARDATA_ANY(fc_string);
	(void) pg_verify_mbstr(fc_src_encoding, fc_src_str, fc_len, false);

	/* 执行转换 */
	fc_dest_str = (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, fc_src_str),
												  fc_len,
												  fc_src_encoding,
												  fc_dest_encoding);

	/*更新长度，如果实际发生了转换 **/
	if (fc_dest_str != fc_src_str)
		fc_len = strlen(fc_dest_str);

	/*
	 * 构建 bytea 数据类型结构。
	 */
	fc_retval = (bytea *) palloc(fc_len + VARHDRSZ);
	SET_VARSIZE(fc_retval, fc_len + VARHDRSZ);
	memcpy(VARDATA(fc_retval), fc_dest_str, fc_len);

	if (fc_dest_str != fc_src_str)
		pfree(fc_dest_str);

	/* 如果由 toaster 分配了内存，则释放内存 */
	PG_FREE_IF_COPY(fc_string, 0);

	PG_RETURN_BYTEA_P(fc_retval);
}

/*
 * 获取在指定编码中被视为文本的字符串的长度。 
 * 如果数据在该编码中无效，则会引发错误。 
 *
 * INT4 长度 (BYTEA 字符串，NAME src_encoding_name)
 */
Datum length_in_encoding(PG_FUNCTION_ARGS)
{
	bytea	   *fc_string = PG_GETARG_BYTEA_PP(0);
	char	   *fc_src_encoding_name = NameStr(*PG_GETARG_NAME(1));
	int			fc_src_encoding = pg_char_to_encoding(fc_src_encoding_name);
	const char *fc_src_str;
	int			fc_len;
	int			fc_retval;

	if (fc_src_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid encoding name \"%s\"",
						fc_src_encoding_name)));

	fc_len = VARSIZE_ANY_EXHDR(fc_string);
	fc_src_str = VARDATA_ANY(fc_string);

	fc_retval = pg_verify_mbstr_len(fc_src_encoding, fc_src_str, fc_len, false);

	PG_RETURN_INT32(fc_retval);
}

/*
 * 获取指定编码中的最大多字节字符长度。
 *
 * 注意，编码是以数字形式指定的，而不是如上所述的名称。
 */
Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS)
{
	int			fc_encoding = PG_GETARG_INT32(0);

	if (PG_VALID_ENCODING(fc_encoding))
		PG_RETURN_INT32(pg_wchar_table[fc_encoding].maxmblen);
	else
		PG_RETURN_NULL();
}

/*
 * 将客户端编码转换为服务器编码。
 *
 * 请参阅本文件顶部关于字符串转换函数的说明。
 */
char * pg_client_to_server(const char *fc_s, int fc_len)
{
	return pg_any_to_server(fc_s, fc_len, ClientEncoding->encoding);
}

/*
 * 将任何编码转换为服务器编码。
 *
 * 请参阅本文件顶部关于字符串转换函数的说明。
 *
 * 与其他字符串转换函数不同，即使 encoding == DatabaseEncoding->encoding，也会应用验证。这是因为此函数用于处理来自数据库外部的数据，我们绝不希望仅仅假设数据是有效的。
 */
char * pg_any_to_server(const char *fc_s, int fc_len, int fc_encoding)
{
	if (fc_len <= 0)
		return unconstify(char *, fc_s);	/* 空字符串总是有效的 */

	if (fc_encoding == DatabaseEncoding->encoding ||
		fc_encoding == PG_SQL_ASCII)
	{
		/*
		 * 不需要转换，但我们仍然必须验证数据。
		 */
		(void) pg_verify_mbstr(DatabaseEncoding->encoding, fc_s, fc_len, false);
		return unconstify(char *, fc_s);
	}

	if (DatabaseEncoding->encoding == PG_SQL_ASCII)
	{
		/*
		 * 无法进行转换，但我们仍然必须验证数据，
		 * 因为客户端代码可能使用所选 client_encoding 进行了字符串转义。
		 * 如果客户端编码是 ASCII 安全的，那么我们只需在该编码下进行简单的验证。
		 * 对于 ASCII 不安全的编码，我们面临一个问题：我们不敢将此类数据传递给解析器，但我们没有办法进行转换。
		 * 我们的折衷是，如果数据包含任何非 ASCII 字符，则拒绝该数据。
		 */
		if (PG_VALID_BE_ENCODING(fc_encoding))
			(void) pg_verify_mbstr(fc_encoding, fc_s, fc_len, false);
		else
		{
			int			fc_i;

			for (fc_i = 0; fc_i < fc_len; fc_i++)
			{
				if (fc_s[fc_i] == '\0' || IS_HIGHBIT_SET(fc_s[fc_i]))
					ereport(ERROR,
							(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
							 errmsg("invalid byte value for encoding \"%s\": 0x%02x",
									pg_enc2name_tbl[PG_SQL_ASCII].name,
									(unsigned char) fc_s[fc_i])));
			}
		}
		return unconstify(char *, fc_s);
	}

	/* 如果可以使用缓存的转换函数则是快速路径 */
	if (fc_encoding == ClientEncoding->encoding)
		return fc_perform_default_encoding_conversion(fc_s, fc_len, true);

	/* 一般情况...在事务外将无法工作 */
	return (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, fc_s),
											  fc_len,
											  fc_encoding,
											  DatabaseEncoding->encoding);
}

/*
 * 将服务器编码转换为客户端编码。
 *
 * 请参阅本文件顶部关于字符串转换函数的说明。
 */
char * pg_server_to_client(const char *fc_s, int fc_len)
{
	return pg_server_to_any(fc_s, fc_len, ClientEncoding->encoding);
}

/*
 * 将服务器编码转换为任何编码。
 *
 * 请参阅本文件顶部关于字符串转换函数的说明。
 */
char * pg_server_to_any(const char *fc_s, int fc_len, int fc_encoding)
{
	if (fc_len <= 0)
		return unconstify(char *, fc_s);	/* 空字符串总是有效的 */

	if (fc_encoding == DatabaseEncoding->encoding ||
		fc_encoding == PG_SQL_ASCII)
		return unconstify(char *, fc_s);	/* 假设数据是有效的 */

	if (DatabaseEncoding->encoding == PG_SQL_ASCII)
	{
		/* 无法进行转换，但我们必须验证结果 */
		(void) pg_verify_mbstr(fc_encoding, fc_s, fc_len, false);
		return unconstify(char *, fc_s);
	}

	/* 如果可以使用缓存的转换函数则是快速路径 */
	if (fc_encoding == ClientEncoding->encoding)
		return fc_perform_default_encoding_conversion(fc_s, fc_len, false);

	/* 一般情况...在事务外将无法工作 */
	return (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, fc_s),
											  fc_len,
											  DatabaseEncoding->encoding,
											  fc_encoding);
}

/*
 * 执行使用缓存 FmgrInfo 的默认编码转换。
 * 由于此函数完全不访问数据库，因此在事务外安全调用。
 * 如果没有通过 SetClientEncoding() 设置转换，则不执行转换。
 */
static char * fc_perform_default_encoding_conversion(const char *fc_src, int fc_len,
									bool fc_is_client_to_server)
{
	char	   *fc_result;
	int			fc_src_encoding,
				fc_dest_encoding;
	FmgrInfo   *fc_flinfo;

	if (fc_is_client_to_server)
	{
		fc_src_encoding = ClientEncoding->encoding;
		fc_dest_encoding = DatabaseEncoding->encoding;
		fc_flinfo = ToServerConvProc;
	}
	else
	{
		fc_src_encoding = DatabaseEncoding->encoding;
		fc_dest_encoding = ClientEncoding->encoding;
		fc_flinfo = ToClientConvProc;
	}

	if (fc_flinfo == NULL)
		return unconstify(char *, fc_src);

	/*
	 * 为转换结果分配空间，要注意整数溢出。
	 * 请参阅 pg_do_encoding_conversion 中的注释。
	 */
	if ((Size) fc_len >= (MaxAllocHugeSize / (Size) MAX_CONVERSION_GROWTH))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("out of memory"),
				 errdetail("String of %d bytes is too long for encoding conversion.",
						   fc_len)));

	fc_result = (char *)
		MemoryContextAllocHuge(CurrentMemoryContext,
							   (Size) fc_len * MAX_CONVERSION_GROWTH + 1);

	FunctionCall6(fc_flinfo,
				  Int32GetDatum(fc_src_encoding),
				  Int32GetDatum(fc_dest_encoding),
				  CStringGetDatum(fc_src),
				  CStringGetDatum(fc_result),
				  Int32GetDatum(fc_len),
				  BoolGetDatum(false));

	/*
	 * 如果可能有很多额外空间，则释放额外空间——请参阅 
	 * pg_do_encoding_conversion 中的注释。
	 */
	if (fc_len > 1000000)
	{
		Size		fc_resultlen = strlen(fc_result);

		if (fc_resultlen >= MaxAllocSize)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("out of memory"),
					 errdetail("String of %d bytes is too long for encoding conversion.",
							   fc_len)));

		fc_result = (char *) repalloc(fc_result, fc_resultlen + 1);
	}

	return fc_result;
}

/*
 * 将单个 Unicode 代码点转换为服务器编码中的字符串。
 *
 * 由 "c" 给出的代码点被转换并存储在 *s 中，*s 必须至少
 * 有 MAX_UNICODE_EQUIVALENT_STRING+1 字节可用。
 * 输出将有一个尾随的 '\0'。如果无法执行转换，则会引发错误。
 *
 * 请注意，这依赖于先前查找任何所需的转换函数。
 * 这部分是为了速度，但主要是因为解析器可能在任何事务外部调用此函数，或在一个中止的事务中。
 */
void pg_unicode_to_server(pg_wchar fc_c, unsigned char *fc_s)
{
	unsigned char fc_c_as_utf8[MAX_MULTIBYTE_CHAR_LEN + 1];
	int			fc_c_as_utf8_len;
	int			fc_server_encoding;

	/*
	 * 如果无效的 Unicode 代码点则抱怨。
	 * 这里的 errcode 选择是有争议的，但实际上我们的调用者应该已经检查过这一点。
	 */
	if (!is_valid_unicode_codepoint(fc_c))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("invalid Unicode code point")));

	/* 否则，如果在 ASCII 范围内，则转换是微不足道的 */
	if (fc_c <= 0x7F)
	{
		fc_s[0] = (unsigned char) fc_c;
		fc_s[1] = '\0';
		return;
	}

	/* 如果服务器编码是 UTF-8，我们只需重新格式化代码 */
	fc_server_encoding = GetDatabaseEncoding();
	if (fc_server_encoding == PG_UTF8)
	{
		unicode_to_utf8(fc_c, fc_s);
		fc_s[pg_utf_mblen(fc_s)] = '\0';
		return;
	}

	/* 对于所有其他情况，我们必须有可用的转换函数 */
	if (Utf8ToServerConvProc == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("conversion between %s and %s is not supported",
						pg_enc2name_tbl[PG_UTF8].name,
						GetDatabaseEncodingName())));

	/* 构建 UTF-8 源字符串 */
	unicode_to_utf8(fc_c, fc_c_as_utf8);
	fc_c_as_utf8_len = pg_utf_mblen(fc_c_as_utf8);
	fc_c_as_utf8[fc_c_as_utf8_len] = '\0';

	/* 转换，或者如果无法转换则抛出错误 */
	FunctionCall6(Utf8ToServerConvProc,
				  Int32GetDatum(PG_UTF8),
				  Int32GetDatum(fc_server_encoding),
				  CStringGetDatum(fc_c_as_utf8),
				  CStringGetDatum(fc_s),
				  Int32GetDatum(fc_c_as_utf8_len),
				  BoolGetDatum(false));
}


/* 将多字节字符串转换为 wchar */
int pg_mb2wchar(const char *fc_from, pg_wchar *fc_to)
{
	return pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len((const unsigned char *) fc_from, fc_to, strlen(fc_from));
}

/* 将多字节字符串转换为指定长度的 wchar */
int pg_mb2wchar_with_len(const char *fc_from, pg_wchar *fc_to, int fc_len)
{
	return pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len((const unsigned char *) fc_from, fc_to, fc_len);
}

/* 同样，适用于任何编码 */
int pg_encoding_mb2wchar_with_len(int fc_encoding,
							  const char *fc_from, pg_wchar *fc_to, int fc_len)
{
	return pg_wchar_table[fc_encoding].mb2wchar_with_len((const unsigned char *) fc_from, fc_to, fc_len);
}

/* 将 wchar 字符串转换为多字节 */
int pg_wchar2mb(const pg_wchar *fc_from, char *fc_to)
{
	return pg_wchar_table[DatabaseEncoding->encoding].wchar2mb_with_len(fc_from, (unsigned char *) fc_to, pg_wchar_strlen(fc_from));
}

/* 将 wchar 字符串转换为指定长度的多字节 */
int pg_wchar2mb_with_len(const pg_wchar *fc_from, char *fc_to, int fc_len)
{
	return pg_wchar_table[DatabaseEncoding->encoding].wchar2mb_with_len(fc_from, (unsigned char *) fc_to, fc_len);
}

/* 同样，适用于任何编码 */
int pg_encoding_wchar2mb_with_len(int fc_encoding,
							  const pg_wchar *fc_from, char *fc_to, int fc_len)
{
	return pg_wchar_table[fc_encoding].wchar2mb_with_len(fc_from, (unsigned char *) fc_to, fc_len);
}

/* 返回多字节字符的字节长度 */
int pg_mblen(const char *fc_mbstr)
{
	return pg_wchar_table[DatabaseEncoding->encoding].mblen((const unsigned char *) fc_mbstr);
}

/* 返回多字节字符的显示长度 */
int pg_dsplen(const char *fc_mbstr)
{
	return pg_wchar_table[DatabaseEncoding->encoding].dsplen((const unsigned char *) fc_mbstr);
}

 /* 返回多字节字符串的长度（用wchar计数） */
int pg_mbstrlen(const char *fc_mbstr)
{
	int			fc_len = 0;

	 /* 针对单字节编码的优化 */
	if (pg_database_encoding_max_length() == 1)
		return strlen(fc_mbstr);

	while (*fc_mbstr)
	{
		fc_mbstr += pg_mblen(fc_mbstr);
		fc_len++;
	}
	return fc_len;
}

 /* 返回多字节字符串的长度（用wchar计数）
 * （不一定以NULL结尾）
 */
int pg_mbstrlen_with_len(const char *fc_mbstr, int fc_limit)
{
	int			fc_len = 0;

	 /* 针对单字节编码的优化 */
	if (pg_database_encoding_max_length() == 1)
		return fc_limit;

	while (fc_limit > 0 && *fc_mbstr)
	{
		int			fc_l = pg_mblen(fc_mbstr);

		fc_limit -= fc_l;
		fc_mbstr += fc_l;
		fc_len++;
	}
	return fc_len;
}

 /*
 * 返回多字节字符串的字节长度
 * （不一定以NULL结尾）
 * 不大于限制。
 * 此函数不打破多字节字符边界。
 */
int pg_mbcliplen(const char *fc_mbstr, int fc_len, int fc_limit)
{
	return pg_encoding_mbcliplen(DatabaseEncoding->encoding, fc_mbstr,
								 fc_len, fc_limit);
}

 /*
 * 带有指定编码的pg_mbcliplen
 */
int pg_encoding_mbcliplen(int fc_encoding, const char *fc_mbstr,
					  int fc_len, int fc_limit)
{
	mblen_converter fc_mblen_fn;
	int			fc_clen = 0;
	int			fc_l;

	 /* 针对单字节编码的优化 */
	if (pg_encoding_max_length(fc_encoding) == 1)
		return fc_cliplen(fc_mbstr, fc_len, fc_limit);

	fc_mblen_fn = pg_wchar_table[fc_encoding].mblen;

	while (fc_len > 0 && *fc_mbstr)
	{
		fc_l = (*fc_mblen_fn) ((const unsigned char *) fc_mbstr);
		if ((fc_clen + fc_l) > fc_limit)
			break;
		fc_clen += fc_l;
		if (fc_clen == fc_limit)
			break;
		fc_len -= fc_l;
		fc_mbstr += fc_l;
	}
	return fc_clen;
}

 /*
 * 与pg_mbcliplen类似，但限制参数指定的是
 * 字符长度，而不是字节长度。
 */
int pg_mbcharcliplen(const char *fc_mbstr, int fc_len, int fc_limit)
{
	int			fc_clen = 0;
	int			fc_nch = 0;
	int			fc_l;

	 /* 针对单字节编码的优化 */
	if (pg_database_encoding_max_length() == 1)
		return fc_cliplen(fc_mbstr, fc_len, fc_limit);

	while (fc_len > 0 && *fc_mbstr)
	{
		fc_l = pg_mblen(fc_mbstr);
		fc_nch++;
		if (fc_nch > fc_limit)
			break;
		fc_clen += fc_l;
		fc_len -= fc_l;
		fc_mbstr += fc_l;
	}
	return fc_clen;
}

 /* 针对任何单字节编码的mbcliplen */
static int fc_cliplen(const char *fc_str, int fc_len, int fc_limit)
{
	int			fc_l = 0;

	fc_len = Min(fc_len, fc_limit);
	while (fc_l < fc_len && fc_str[fc_l])
		fc_l++;
	return fc_l;
}

void SetDatabaseEncoding(int fc_encoding)
{
	if (!PG_VALID_BE_ENCODING(fc_encoding))
		elog(ERROR, "invalid database encoding: %d", fc_encoding);

	DatabaseEncoding = &pg_enc2name_tbl[fc_encoding];
	Assert(DatabaseEncoding->encoding == fc_encoding);
}

void SetMessageEncoding(int fc_encoding)
{
	 /* 一些调用发生在我们可以elog()之前！ */
	Assert(PG_VALID_ENCODING(fc_encoding));

	MessageEncoding = &pg_enc2name_tbl[fc_encoding];
	Assert(MessageEncoding->encoding == fc_encoding);
}

#ifdef ENABLE_NLS
 /*
 * 进行一次bind_textdomain_codeset()调用，将pg_enc转换为gettext
 * 代码集。对于MULE_INTERNAL（gettext未知的编码）会失败；也可能
 * 因为gettext内部原因导致失败，比如内存不足。
 */
static bool fc_raw_pg_bind_textdomain_codeset(const char *fc_domainname, int fc_encoding)
{
	bool		fc_elog_ok = (CurrentMemoryContext != NULL);
	int			fc_i;

	for (fc_i = 0; pg_enc2gettext_tbl[fc_i].name != NULL; fc_i++)
	{
		if (pg_enc2gettext_tbl[fc_i].encoding == fc_encoding)
		{
			if (bind_textdomain_codeset(fc_domainname,
										pg_enc2gettext_tbl[fc_i].name) != NULL)
				return true;

			if (fc_elog_ok)
				elog(LOG, "bind_textdomain_codeset failed");
			else
				write_stderr("bind_textdomain_codeset failed");

			break;
		}
	}

	return false;
}

 /*
 * 将gettext消息域绑定到与数据库
 * 编码相对应的代码集。对于SQL_ASCII，应绑定到
 * LC_CTYPE所暗示的代码集。返回新设置所暗示的MessageEncoding。
 *
 * 在大多数平台上，gettext的默认值是由LC_CTYPE暗示的代码集。
 * 当与数据库编码匹配时，我们无需做任何事情。在
 * CREATE DATABASE中，我们强制或信任区域设置的代码集与
 * 数据库编码匹配，C区域除外。（在Windows上，我们也允许
 * 在UTF8编码下存在差异。）对于C区域，明确绑定
 * gettext到正确的代码集。
 *
 * 在Windows上，gettext的默认值是Windows ANSI代码页。这是一个
 * 为将字符串传递给Windows ANSI API的软件提供的方便的出发点，
 * 但我们并不这样做。强制gettext使用数据库编码，否则，
 * 使用LC_CTYPE编码，就像在其他平台上一样。
 *
 * 此函数在elog()和palloc()可用之前被调用。
 */
int pg_bind_textdomain_codeset(const char *fc_domainname)
{
	bool		fc_elog_ok = (CurrentMemoryContext != NULL);
	int			fc_encoding = GetDatabaseEncoding();
	int			fc_new_msgenc;

#ifndef WIN32
	const char *fc_ctype = setlocale(LC_CTYPE, NULL);

	if (pg_strcasecmp(fc_ctype, "C") == 0 || pg_strcasecmp(fc_ctype, "POSIX") == 0)
#endif
		if (fc_encoding != PG_SQL_ASCII &&
			fc_raw_pg_bind_textdomain_codeset(fc_domainname, fc_encoding))
			return fc_encoding;

	fc_new_msgenc = pg_get_encoding_from_locale(NULL, fc_elog_ok);
	if (fc_new_msgenc < 0)
		fc_new_msgenc = PG_SQL_ASCII;

#ifdef WIN32
	if (!raw_pg_bind_textdomain_codeset(domainname, new_msgenc))
		 /* 失败时，旧的消息编码保持有效。 */
		return GetMessageEncoding();
#endif

	return fc_new_msgenc;
}
#endif

 /*
 * 数据库编码，也称为服务器编码，表示存储在文本类数据类型中的
 * 数据的编码。受影响的类型包括
 * cstring、text、varchar、name、xml和json。
 */
int GetDatabaseEncoding(void)
{
	return DatabaseEncoding->encoding;
}

const char * GetDatabaseEncodingName(void)
{
	return DatabaseEncoding->name;
}

Datum getdatabaseencoding(PG_FUNCTION_ARGS)
{
	return DirectFunctionCall1(namein, CStringGetDatum(DatabaseEncoding->name));
}

Datum pg_client_encoding(PG_FUNCTION_ARGS)
{
	return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
}

Datum PG_char_to_encoding(PG_FUNCTION_ARGS)
{
	Name		fc_s = PG_GETARG_NAME(0);

	PG_RETURN_INT32(pg_char_to_encoding(NameStr(*fc_s)));
}

Datum PG_encoding_to_char(PG_FUNCTION_ARGS)
{
	int32		fc_encoding = PG_GETARG_INT32(0);
	const char *fc_encoding_name = pg_encoding_to_char(fc_encoding);

	return DirectFunctionCall1(namein, CStringGetDatum(fc_encoding_name));
}

 /*
 * gettext()以此编码返回消息。这通常与
 * 数据库编码匹配，但对于SQL_ASCII数据库、未连接到数据库的进程以及
 * 缺乏iconv支持的数据库编码（MULE_INTERNAL）则有所不同。
 */
int GetMessageEncoding(void)
{
	return MessageEncoding->encoding;
}


 /*
 * 通用字符递增器函数。
 *
 * 由于不知道所使用编码的属性，我们只是
 * 一直递增最后一个字节，直到获得有效编码的结果，
 * 或者我们没有值可尝试。我们不打算尝试递增
 * 高位字节，因此对于更宽字符的运行时不会增长。
 * （如果我们尝试这样做，我们需要考虑255
 * 不是编码中有效的最终字节的可能性。）
 */
static bool pg_generic_charinc(unsigned char *fc_charptr, int fc_len)
{
	unsigned char *fc_lastbyte = fc_charptr + fc_len - 1;
	mbchar_verifier fc_mbverify;

	 /* 我们可以直接调用字符验证器。 */
	fc_mbverify = pg_wchar_table[GetDatabaseEncoding()].mbverifychar;

	while (*fc_lastbyte < (unsigned char) 255)
	{
		(*fc_lastbyte)++;
		if ((*fc_mbverify) (fc_charptr, fc_len) == fc_len)
			return true;
	}

	return false;
}

 /*
 * UTF-8字符递增器函数。
 *
 * 对于小于0x7F的单字节字符，我们只是递增该字节。
 *
 * 对于多字节字符，除了第一个字节外，每个字节都必须在0x80
 * 到0xBF之间；第一个字节必须在0xC0到0xF4之间。我们递增
 * 最后一个尚未达到其最大值的字节。如果找不到小于
 * 最大允许值的字节，我们就失败。我们还需要一些特殊逻辑
 * 跳过用于代理对处理的区域，因为这些区域在有效的UTF-8中
 * 不应出现。
 *
 * 注意，我们不会将低位字节重置为最小值，因为
 * 我们无法承担进行全面搜索（请参见make_greater_string）。
 */
static bool pg_utf8_increment(unsigned char *fc_charptr, int fc_length)
{
	unsigned char fc_a;
	unsigned char fc_limit;

	switch (fc_length)
	{
		default:
			/* 当前拒绝长度为5和6 */
			return false;
		case 4:
			fc_a = fc_charptr[3];
			if (fc_a < 0xBF)
			{
				fc_charptr[3]++;
				break;
			}
			/* FALL THRU */
		case 3:
			fc_a = fc_charptr[2];
			if (fc_a < 0xBF)
			{
				fc_charptr[2]++;
				break;
			}
			/* FALL THRU */
		case 2:
			fc_a = fc_charptr[1];
			switch (*fc_charptr)
			{
				case 0xED:
					fc_limit = 0x9F;
					break;
				case 0xF4:
					fc_limit = 0x8F;
					break;
				default:
					fc_limit = 0xBF;
					break;
			}
			if (fc_a < fc_limit)
			{
				fc_charptr[1]++;
				break;
			}
			/* FALL THRU */
		case 1:
			fc_a = *fc_charptr;
			if (fc_a == 0x7F || fc_a == 0xDF || fc_a == 0xEF || fc_a == 0xF4)
				return false;
			fc_charptr[0]++;
			break;
	}

	return true;
}

 /*
 * EUC-JP字符递增器函数。
 *
 * 如果序列以SS2（0x8e）开头，它必须是一个两字节序列
 * 代表JIS X 0201字符，第二个字节的范围在
 * 0xa1到0xdf之间。如果小于0xdf，我们只是递增最后一个字节，
 * 否则重写整个序列为0xa1 0xa1。
 *
 * 如果序列以SS3（0x8f）开头，它必须是一个三字节序列
 * 其中最后两个字节的范围在0xa1到0xfe之间。如果可能的话，递增
 * 最后一个字节，否则递增倒数第二个字节。
 *
 * 如果序列以其他值开头且其MSB被设置，它必须是一个代表
 * JIS X 0208字符的两字节序列，两个字节的范围在
 * 0xa1到0xfe之间。如果可能的话，递增最后一个字节，
 * 否则递增倒数第二个字节。
 *
 * 否则，序列是一个单字节ASCII字符。它
 * 被递增到0x7f。
 */
static bool pg_eucjp_increment(unsigned char *fc_charptr, int fc_length)
{
	unsigned char fc_c1,
				fc_c2;
	int			fc_i;

	fc_c1 = *fc_charptr;

	switch (fc_c1)
	{
		case SS2:				/* JIS X 0201 */
			if (fc_length != 2)
				return false;

			fc_c2 = fc_charptr[1];

			if (fc_c2 >= 0xdf)
				fc_charptr[0] = fc_charptr[1] = 0xa1;
			else if (fc_c2 < 0xa1)
				fc_charptr[1] = 0xa1;
			else
				fc_charptr[1]++;
			break;

		case SS3:				/* JIS X 0212 */
			if (fc_length != 3)
				return false;

			for (fc_i = 2; fc_i > 0; fc_i--)
			{
				fc_c2 = fc_charptr[fc_i];
				if (fc_c2 < 0xa1)
				{
					fc_charptr[fc_i] = 0xa1;
					return true;
				}
				else if (fc_c2 < 0xfe)
				{
					fc_charptr[fc_i]++;
					return true;
				}
			}

			 /* 超出3字节编码区域 */
			return false;

		default:
			if (IS_HIGHBIT_SET(fc_c1)) /* JIS X 0208? */
			{
				if (fc_length != 2)
					return false;

				for (fc_i = 1; fc_i >= 0; fc_i--)
				{
					fc_c2 = fc_charptr[fc_i];
					if (fc_c2 < 0xa1)
					{
						fc_charptr[fc_i] = 0xa1;
						return true;
					}
					else if (fc_c2 < 0xfe)
					{
						fc_charptr[fc_i]++;
						return true;
					}
				}

				 /* 超出2字节编码区域 */
				return false;
			}
			else
			{					 /* ASCII，单字节 */
				if (fc_c1 > 0x7e)
					return false;
				(*fc_charptr)++;
			}
			break;
	}

	return true;
}


/*
 * 获取当前数据库编码的字符增量器
 */
mbcharacter_incrementer
pg_database_encoding_character_incrementer(void)
{
	/*
	 * 最终可能最好是向 pg_wchar_table[] 添加一个字段，但现在我们只是使用一个 switch。
	 */
	switch (GetDatabaseEncoding())
	{
		case PG_UTF8:
			return pg_utf8_increment;

		case PG_EUC_JP:
			return pg_eucjp_increment;

		default:
			return pg_generic_charinc;
	}
}

/*
 * 获取当前数据库编码的最大长度
 */
int pg_database_encoding_max_length(void)
{
	return pg_wchar_table[GetDatabaseEncoding()].maxmblen;
}

/*
 * 验证 mbstr 以确保其在当前数据库编码中是有效编码。否则与 pg_verify_mbstr() 相同。
 */
bool pg_verifymbstr(const char *fc_mbstr, int fc_len, bool fc_noError)
{
	return pg_verify_mbstr(GetDatabaseEncoding(), fc_mbstr, fc_len, fc_noError);
}

/*
 * 验证 mbstr 以确保其在指定编码中是有效编码。
 */
bool pg_verify_mbstr(int fc_encoding, const char *fc_mbstr, int fc_len, bool fc_noError)
{
	int			fc_oklen;

	Assert(PG_VALID_ENCODING(fc_encoding));

	fc_oklen = pg_wchar_table[fc_encoding].mbverifystr((const unsigned char *) fc_mbstr, fc_len);
	if (fc_oklen != fc_len)
	{
		if (fc_noError)
			return false;
		report_invalid_encoding(fc_encoding, fc_mbstr + fc_oklen, fc_len - fc_oklen);
	}
	return true;
}

/*
 * 验证 mbstr 以确保其在指定编码中是有效编码。
 *
 * mbstr 不一定是以零终止的；mbstr 的长度由 len 指定。
 *
 * 如果可以，返回编码中的字符串长度。
 * 如果发现问题，当 noError 为 true 时返回 -1；当 noError 为 false 时，使用 ereport() 返回描述性消息。
 *
 * 注意：我们不能在这里使用更快的特定编码 mbverifystr() 函数，因为我们需要计算字符串中的字符数。
 */
int pg_verify_mbstr_len(int fc_encoding, const char *fc_mbstr, int fc_len, bool fc_noError)
{
	mbchar_verifier fc_mbverifychar;
	int			fc_mb_len;

	Assert(PG_VALID_ENCODING(fc_encoding));

	/*
	 * 在单字节编码中，我们只需拒绝 null (\0)。
	 */
	if (pg_encoding_max_length(fc_encoding) <= 1)
	{
		const char *fc_nullpos = memchr(fc_mbstr, 0, fc_len);

		if (fc_nullpos == NULL)
			return fc_len;
		if (fc_noError)
			return -1;
		report_invalid_encoding(fc_encoding, fc_nullpos, 1);
	}

	/* 仅获取函数指针一次 */
	fc_mbverifychar = pg_wchar_table[fc_encoding].mbverifychar;

	fc_mb_len = 0;

	while (fc_len > 0)
	{
		int			fc_l;

		/* 针对 ASCII 子集字符的快速路径 */
		if (!IS_HIGHBIT_SET(*fc_mbstr))
		{
			if (*fc_mbstr != '\0')
			{
				fc_mb_len++;
				fc_mbstr++;
				fc_len--;
				continue;
			}
			if (fc_noError)
				return -1;
			report_invalid_encoding(fc_encoding, fc_mbstr, fc_len);
		}

		fc_l = (*fc_mbverifychar) ((const unsigned char *) fc_mbstr, fc_len);

		if (fc_l < 0)
		{
			if (fc_noError)
				return -1;
			report_invalid_encoding(fc_encoding, fc_mbstr, fc_len);
		}

		fc_mbstr += fc_l;
		fc_len -= fc_l;
		fc_mb_len++;
	}
	return fc_mb_len;
}

/*
 * check_encoding_conversion_args: 检查转换函数的参数
 *
 * “期望”的参数可以是编码 ID 或 -1，以指示调用者将检查是否接受该 ID。
 *
 * 注意：这里的错误并不是面向用户的，因此使用 elog 而不是 ereport 似乎就足够了。此外，我们相信“期望”的编码参数是有效的编码 ID，但我们不相信实际参数。
 */
void check_encoding_conversion_args(int fc_src_encoding,
							   int fc_dest_encoding,
							   int fc_len,
							   int fc_expected_src_encoding,
							   int fc_expected_dest_encoding)
{
	if (!PG_VALID_ENCODING(fc_src_encoding))
		elog(ERROR, "invalid source encoding ID: %d", fc_src_encoding);
	if (fc_src_encoding != fc_expected_src_encoding && fc_expected_src_encoding >= 0)
		elog(ERROR, "expected source encoding \"%s\", but got \"%s\"",
			 pg_enc2name_tbl[fc_expected_src_encoding].name,
			 pg_enc2name_tbl[fc_src_encoding].name);
	if (!PG_VALID_ENCODING(fc_dest_encoding))
		elog(ERROR, "invalid destination encoding ID: %d", fc_dest_encoding);
	if (fc_dest_encoding != fc_expected_dest_encoding && fc_expected_dest_encoding >= 0)
		elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"",
			 pg_enc2name_tbl[fc_expected_dest_encoding].name,
			 pg_enc2name_tbl[fc_dest_encoding].name);
	if (fc_len < 0)
		elog(ERROR, "encoding conversion length must not be negative");
}

/*
 * report_invalid_encoding: 报告无效的多字节字符
 *
 * 注：len是字符串的剩余长度，而不是字符的长度；
 * len必须大于零，因为我们总是检查第一个字节。
 */
void report_invalid_encoding(int fc_encoding, const char *fc_mbstr, int fc_len)
{
	int			fc_l = pg_encoding_mblen(fc_encoding, fc_mbstr);
	char		fc_buf[8 * 5 + 1];
	char	   *fc_p = fc_buf;
	int			fc_j,
				fc_jlimit;

	fc_jlimit = Min(fc_l, fc_len);
	fc_jlimit = Min(fc_jlimit, 8);	/* 防止缓冲区溢出 */

	for (fc_j = 0; fc_j < fc_jlimit; fc_j++)
	{
		fc_p += sprintf(fc_p, "0x%02x", (unsigned char) fc_mbstr[fc_j]);
		if (fc_j < fc_jlimit - 1)
			fc_p += sprintf(fc_p, " ");
	}

	ereport(ERROR,
			(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
			 errmsg("invalid byte sequence for encoding \"%s\": %s",
					pg_enc2name_tbl[fc_encoding].name,
					fc_buf)));
}

/*
 * report_untranslatable_char: 报告无法翻译的字符
 *
 * 注：len是字符串的剩余长度，而不是字符的长度；
 * len必须大于零，因为我们总是检查第一个字节。
 */
void report_untranslatable_char(int fc_src_encoding, int fc_dest_encoding,
						   const char *fc_mbstr, int fc_len)
{
	int			fc_l = pg_encoding_mblen(fc_src_encoding, fc_mbstr);
	char		fc_buf[8 * 5 + 1];
	char	   *fc_p = fc_buf;
	int			fc_j,
				fc_jlimit;

	fc_jlimit = Min(fc_l, fc_len);
	fc_jlimit = Min(fc_jlimit, 8);	/* 防止缓冲区溢出 */

	for (fc_j = 0; fc_j < fc_jlimit; fc_j++)
	{
		fc_p += sprintf(fc_p, "0x%02x", (unsigned char) fc_mbstr[fc_j]);
		if (fc_j < fc_jlimit - 1)
			fc_p += sprintf(fc_p, " ");
	}

	ereport(ERROR,
			(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
			 errmsg("character with byte sequence %s in encoding \"%s\" has no equivalent in encoding \"%s\"",
					fc_buf,
					pg_enc2name_tbl[fc_src_encoding].name,
					pg_enc2name_tbl[fc_dest_encoding].name)));
}


#ifdef WIN32
/*
 * 从MessageEncoding转换为palloc分配的以null结尾的utf16
 * 字符串。如果utf16len不为null，则也传递字符长度。失败时返回NULL。
 * 在MessageEncoding初始化之前，“str”应该仅为ASCII；这将像
 * MessageEncoding为UTF8时那样工作。
 */
WCHAR *
pgwin32_message_to_UTF16(const char *str, int len, int *utf16len)
{
	int			msgenc = GetMessageEncoding();
	WCHAR	   *utf16;
	int			dstlen;
	UINT		codepage;

	if (msgenc == PG_SQL_ASCII)
		/* 无法进行转换，且SQL_ASCII永远不是utf16。 */
		return NULL;

	codepage = pg_enc2name_tbl[msgenc].codepage;

	/*
	 * 如果有相应的代码页，则直接使用MultiByteToWideChar，
	 * 否则通过UTF8进行双重转换。双重转换是必要的，
	 * 例如，在一个ENCODING=LATIN8，LC_CTYPE=C的数据库中。
	 */
	if (codepage != 0)
	{
		utf16 = (WCHAR *) palloc(sizeof(WCHAR) * (len + 1));
		dstlen = MultiByteToWideChar(codepage, 0, str, len, utf16, len);
		utf16[dstlen] = (WCHAR) 0;
	}
	else
	{
		char	   *utf8;

		/*
		 * XXX pg_do_encoding_conversion()需要一个事务。
		 * 如果没有事务，希望输入是有效的UTF8。
		 */
		if (IsTransactionState())
		{
			utf8 = (char *) pg_do_encoding_conversion((unsigned char *) str,
													  len,
													  msgenc,
													  PG_UTF8);
			if (utf8 != str)
				len = strlen(utf8);
		}
		else
			utf8 = (char *) str;

		utf16 = (WCHAR *) palloc(sizeof(WCHAR) * (len + 1));
		dstlen = MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, len);
		utf16[dstlen] = (WCHAR) 0;

		if (utf8 != str)
			pfree(utf8);
	}

	if (dstlen == 0 && len > 0)
	{
		pfree(utf16);
		return NULL;			/* 错误 */
	}

	if (utf16len)
		*utf16len = dstlen;
	return utf16;
}

#endif							/* WIN32 */
